package org.codehaus.mojo.versions.utils;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import static java.util.Optional.ofNullable;

/**
 * Utility methods to help with regex manipulation.
 *
 * @author Stephen Connolly
 * @since 1.0-alpha-3
 */
public final class RegexUtils {
    /**
     * The end of a regex literal sequence.
     *
     * @since 1.0-alpha-1
     */
    public static final String REGEX_QUOTE_END = "\\E";

    /**
     * The start of a regex literal sequence.
     *
     * @since 1.0-alpha-1
     */
    public static final String REGEX_QUOTE_START = "\\Q";

    /**
     * Escape the escapes.
     *
     * @since 1.0-alpha-1
     */
    public static final String REGEX_QUOTE_END_ESCAPED = REGEX_QUOTE_END + '\\' + REGEX_QUOTE_END + REGEX_QUOTE_START;

    private RegexUtils() {
        throw new IllegalAccessError("Utility classes should never be instantiated");
    }

    /**
     * Takes a string and returns the regex that will match that string exactly.
     *
     * @param s The string to match.
     * @return The regex that will match the string exactly.
     * @since 1.0-alpha-1
     */
    public static String quote(String s) {
        int i = s.indexOf(REGEX_QUOTE_END);
        if (i == -1) {
            // we're safe as nobody has a crazy \E in the string
            return REGEX_QUOTE_START + s + REGEX_QUOTE_END;
        }

        // damn there's at least one \E in the string
        StringBuilder sb = new StringBuilder(s.length() + 32);
        // each escape-escape takes 10 chars...
        // hope there's less than 4 of them

        sb.append(REGEX_QUOTE_START);
        int pos = 0;
        do {
            // we are safe from pos to i
            sb.append(s, pos, i);
            // now escape-escape
            sb.append(REGEX_QUOTE_END_ESCAPED);
            // move the working start
            pos = i + REGEX_QUOTE_END.length();
            i = s.indexOf(REGEX_QUOTE_END, pos);
        } while (i != -1);

        sb.append(s.substring(pos));
        sb.append(REGEX_QUOTE_END);

        return sb.toString();
    }

    public static int getWildcardScore(String wildcardRule) {
        int score = 0;
        if (wildcardRule != null) {
            for (char c : wildcardRule.toCharArray()) {
                if (c == '?') {
                    score++;
                } else if (c == '*') {
                    score += 1000;
                }
            }
        }
        return score;
    }

    /**
     * Converts a wildcard rule to a regex rule.
     *
     * @param wildcardRule the wildcard rule, may be {@code null}
     * @param exactMatch <code>true</code> results in an regex that will match the entire string, while
     *            <code>false</code> will match the start of the string.
     * @return The regex rule.
     */
    public static String convertWildcardsToRegex(String wildcardRule, boolean exactMatch) {
        StringBuilder regex = new StringBuilder();
        final int wildcardLength = ofNullable(wildcardRule).map(String::length).orElse(0);
        for (int index = 0, nextIndex = 0; index < wildcardLength; index = nextIndex + 1) {
            final int nextQ = wildcardRule.indexOf('?', index);
            final int nextS = wildcardRule.indexOf('*', index);
            if (nextQ == -1 && nextS == -1) {
                regex.append(quote(wildcardRule.substring(index)));
                break;
            }
            if (nextQ == -1) {
                nextIndex = nextS;
            } else if (nextS == -1) {
                nextIndex = nextQ;
            } else {
                nextIndex = Math.min(nextQ, nextS);
            }
            if (index < nextIndex) {
                // we have some characters to match
                regex.append(quote(wildcardRule.substring(index, nextIndex)));
            }
            char c = wildcardRule.charAt(nextIndex);
            if (c == '?') {
                regex.append('.');
            } else {
                regex.append(".*");
            }
        }
        if (!exactMatch) {
            regex.append(".*");
        }
        return regex.toString();
    }
}
